package com.gitee.wsl.ext.map

/**
 * Returns a list containing the **non null** results of applying the given
 * [transform] function to each entry in the original map.
 */
inline fun <K, V, R : Any> Map<K, V>.mapNotNullValues(
    transform: (Map.Entry<K, V>) -> R?
): Map<K, R> {
    val destination = mutableMapOf<K, R>()
    for (entry in entries) {
        val value = transform(entry)
        if (value != null) {
            destination[entry.key] = value
        }
    }
    return destination
}


inline fun <K, V, R : Any> Map<out K, V>.mapKeysNotNull(
    transform: (Map.Entry<K, V>) -> R?,
): Map<R, V> = mapNotNull {
    (transform(it) ?: return@mapNotNull null) to it.value
}.toMap()


fun <K, V> linkedHashMapOf(vararg pairs: Pair<K, V>): LinkedHashMap<K, V> = LinkedHashMap<K, V>().also { for ((key, value) in pairs) it[key] = value }
fun <K, V> Iterable<Pair<K, V>>.toLinkedMap(): LinkedHashMap<K, V> = LinkedHashMap<K, V>().also { for ((key, value) in this) it[key] = value }

fun <K, V> Map<K, V>.flip(): Map<V, K> = this.map { Pair(it.value, it.key) }.toMap()
fun <T> List<T>.countMap(): Map<T, Int> = LinkedHashMap<T, Int>().also { for (key in this) it.incr(key, +1) }


fun <K, V> LinkedHashMap<K, V>.removeFirst(): Pair<K, V> {
    val key = keys.first()
    val value = values.first()
    remove(key)
    return key to value
}

fun <K, V> LinkedHashMap<K, V>.removeLast(): Pair<K, V> {
    val key = keys.last()
    val value = values.last()
    remove(key)
    return key to value
}

fun <K, V> LinkedHashMap<K, V>.removeLastOrNull(): Pair<K?, V?> {
    val key = keys.lastOrNull()
    val value = values.lastOrNull()
    remove(key)
    return key to value
}

//fun <K,V> MutableMap<K,V>.computeIfAbsent(key:K,block:()->V):V{
//    var base = get(key)
//    if(base == null){
//        base = block()
//        put(key, base)
//    }
//    return base!!
//}

fun <K, V> MutableMap<K,V>.computeIfAbsent(key: K, compute: (K) -> V): V {
    val oldValue = get(key)
    if (oldValue == null) {
        val newValue = compute(key)
        put(key, newValue)
        return newValue
    }

    return oldValue
}


/**
 * Retrieves the value associated with the given key and applies a mapping function to it.
 *
 * If the key is present in the map, the associated value is passed to the ifNotNullMapper function
 * and the result is returned. If the key is not present, null is returned. This method provides a convenient
 * way to transform values while retrieving them from the map.
 *
 * @param key the key whose associated value is to be retrieved and mapped.
 * @param ifNotNullMapper a function to apply to the value if the key is present.
 * @return the result of applying the mapping function to the value, or null if the key is not present.
 */
 inline fun <K, V, NV> Map<K, V>.getAndMap(key: K, ifNotNullMapper: (V) -> NV): NV? {
    val v = this[key] ?: return null
    return ifNotNullMapper.invoke(v)
}

fun <K, V> entry(key: K, value: V): Map.Entry<K, V> = object : Map.Entry<K, V> {
    override val key = key
    override val value = value
}

/**
 * Transforms this map into a new mutable map using the specified key and value mappers.
 *
 * The keyMapper function is applied to each entry to produce the new keys, and the valueMapper function is applied
 * to produce the new values. Entries for which the key or value mapper returns null are skipped. This allows for
 * flexible transformation of maps, including filtering and changing the types of keys and values.
 *
 * @param keyMapper a function that maps entries to new keys.
 * @param valueMapper a function that maps entries to new values.
 * @param mapGen a function to generate the resulting mutable map.
 * @return a new mutable map with the transformed keys and values.
 */
public inline fun <K, V, NK, NV> Map<K, V>.map(
    keyMapper: (Map.Entry<K, V>) -> NK?,
    valueMapper: (Map.Entry<NK, V>) -> NV?,
    mapGen: () -> MutableMap<NK, NV> = ::HashMap,
): MutableMap<NK, NV> {

    val newMap = mapGen()
    for ((key, value) in this) {
        val keyEntry = entry(key, value)
        val newKey = keyMapper(keyEntry) ?: continue
        val valEntry = entry(newKey, value)
        val newValue = valueMapper(valEntry) ?: continue
        newMap[newKey] = newValue
    }
    return newMap
}

